1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkNotNull;
20  import static com.google.common.collect.CollectPreconditions.checkEntryNotNull;
21  import static com.google.common.collect.Iterables.getOnlyElement;
22  
23  import java.io.Serializable;
24  import java.util.Collections;
25  import java.util.EnumMap;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  
30  import javax.annotation.Nullable;
31  
32  /**
33   * GWT emulation of {@link ImmutableMap}.  For non sorted maps, it is a thin
34   * wrapper around {@link java.util.Collections#emptyMap()}, {@link
35   * Collections#singletonMap(Object, Object)} and {@link java.util.LinkedHashMap}
36   * for empty, singleton and regular maps respectively.  For sorted maps, it's
37   * a thin wrapper around {@link java.util.TreeMap}.
38   *
39   * @see ImmutableSortedMap
40   *
41   * @author Hayward Chan
42   */
43  public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
44  
45    ImmutableMap() {}
46  
47    public static <K, V> ImmutableMap<K, V> of() {
48      return ImmutableBiMap.of();
49    }
50  
51    public static <K, V> ImmutableMap<K, V> of(K k1, V v1) {
52      return ImmutableBiMap.of(k1, v1);
53    }
54  
55    public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2) {
56      return new RegularImmutableMap<K, V>(entryOf(k1, v1), entryOf(k2, v2));
57    }
58  
59    public static <K, V> ImmutableMap<K, V> of(
60        K k1, V v1, K k2, V v2, K k3, V v3) {
61      return new RegularImmutableMap<K, V>(
62          entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3));
63    }
64  
65    public static <K, V> ImmutableMap<K, V> of(
66        K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
67      return new RegularImmutableMap<K, V>(
68          entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4));
69    }
70  
71    public static <K, V> ImmutableMap<K, V> of(
72        K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
73      return new RegularImmutableMap<K, V>(entryOf(k1, v1),
74          entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5));
75    }
76  
77    // looking for of() with > 5 entries? Use the builder instead.
78  
79    public static <K, V> Builder<K, V> builder() {
80      return new Builder<K, V>();
81    }
82  
83    static <K, V> Entry<K, V> entryOf(K key, V value) {
84      checkEntryNotNull(key, value);
85      return Maps.immutableEntry(key, value);
86    }
87  
88    public static class Builder<K, V> {
89      final List<Entry<K, V>> entries = Lists.newArrayList();
90  
91      public Builder() {}
92  
93      public Builder<K, V> put(K key, V value) {
94        entries.add(entryOf(key, value));
95        return this;
96      }
97  
98      public Builder<K, V> put(Entry<? extends K, ? extends V> entry) {
99        if (entry instanceof ImmutableEntry) {
100         checkNotNull(entry.getKey());
101         checkNotNull(entry.getValue());
102         @SuppressWarnings("unchecked") // all supported methods are covariant
103         Entry<K, V> immutableEntry = (Entry<K, V>) entry;
104         entries.add(immutableEntry);
105       } else {
106         entries.add(entryOf((K) entry.getKey(), (V) entry.getValue()));
107       }
108       return this;
109     }
110 
111     public Builder<K, V> putAll(Map<? extends K, ? extends V> map) {
112       for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
113         put(entry.getKey(), entry.getValue());
114       }
115       return this;
116     }
117 
118     public ImmutableMap<K, V> build() {
119       return fromEntryList(entries);
120     }
121 
122     private static <K, V> ImmutableMap<K, V> fromEntryList(
123         List<Entry<K, V>> entries) {
124       int size = entries.size();
125       switch (size) {
126         case 0:
127           return of();
128         case 1:
129           Entry<K, V> entry = getOnlyElement(entries);
130           return of(entry.getKey(), entry.getValue());
131         default:
132           @SuppressWarnings("unchecked")
133           Entry<K, V>[] entryArray
134               = entries.toArray(new Entry[entries.size()]);
135           return new RegularImmutableMap<K, V>(entryArray);
136       }
137     }
138   }
139 
140   public static <K, V> ImmutableMap<K, V> copyOf(
141       Map<? extends K, ? extends V> map) {
142     if ((map instanceof ImmutableMap) && !(map instanceof ImmutableSortedMap)) {
143       @SuppressWarnings("unchecked") // safe since map is not writable
144       ImmutableMap<K, V> kvMap = (ImmutableMap<K, V>) map;
145       return kvMap;
146     } else if (map instanceof EnumMap) {
147       EnumMap<?, ?> enumMap = (EnumMap<?, ?>) map;
148       for (Map.Entry<?, ?> entry : enumMap.entrySet()) {
149         checkNotNull(entry.getKey());
150         checkNotNull(entry.getValue());
151       }
152       @SuppressWarnings("unchecked")
153       // immutable collections are safe for covariant casts
154       ImmutableMap<K, V> result = ImmutableEnumMap.asImmutable(new EnumMap(enumMap));
155       return result;
156     }
157 
158     int size = map.size();
159     switch (size) {
160       case 0:
161         return of();
162       case 1:
163         Entry<? extends K, ? extends V> entry
164             = getOnlyElement(map.entrySet());
165         return ImmutableMap.<K, V>of(entry.getKey(), entry.getValue());
166       default:
167         Map<K, V> orderPreservingCopy = Maps.newLinkedHashMap();
168         for (Entry<? extends K, ? extends V> e : map.entrySet()) {
169           orderPreservingCopy.put(
170               checkNotNull(e.getKey()), checkNotNull(e.getValue()));
171         }
172         return new RegularImmutableMap<K, V>(orderPreservingCopy);
173     }
174   }
175 
176   abstract boolean isPartialView();
177 
178   public final V put(K k, V v) {
179     throw new UnsupportedOperationException();
180   }
181 
182   public final V remove(Object o) {
183     throw new UnsupportedOperationException();
184   }
185 
186   public final void putAll(Map<? extends K, ? extends V> map) {
187     throw new UnsupportedOperationException();
188   }
189 
190   public final void clear() {
191     throw new UnsupportedOperationException();
192   }
193 
194   @Override
195   public boolean isEmpty() {
196     return size() == 0;
197   }
198 
199   @Override
200   public boolean containsKey(@Nullable Object key) {
201     return get(key) != null;
202   }
203 
204   @Override
205   public boolean containsValue(@Nullable Object value) {
206     return values().contains(value);
207   }
208 
209   private transient ImmutableSet<Entry<K, V>> cachedEntrySet = null;
210 
211   public final ImmutableSet<Entry<K, V>> entrySet() {
212     if (cachedEntrySet != null) {
213       return cachedEntrySet;
214     }
215     return cachedEntrySet = createEntrySet();
216   }
217 
218   abstract ImmutableSet<Entry<K, V>> createEntrySet();
219 
220   private transient ImmutableSet<K> cachedKeySet = null;
221 
222   public ImmutableSet<K> keySet() {
223     if (cachedKeySet != null) {
224       return cachedKeySet;
225     }
226     return cachedKeySet = createKeySet();
227   }
228 
229   ImmutableSet<K> createKeySet() {
230     return new ImmutableMapKeySet<K, V>(this);
231   }
232 
233   private transient ImmutableCollection<V> cachedValues = null;
234 
235   public ImmutableCollection<V> values() {
236     if (cachedValues != null) {
237       return cachedValues;
238     }
239     return cachedValues = createValues();
240   }
241 
242   // esnickell is editing here
243 
244   // cached so that this.multimapView().inverse() only computes inverse once
245   private transient ImmutableSetMultimap<K, V> multimapView;
246 
247   public ImmutableSetMultimap<K, V> asMultimap() {
248     ImmutableSetMultimap<K, V> result = multimapView;
249     return (result == null) ? (multimapView = createMultimapView()) : result;
250   }
251 
252   private ImmutableSetMultimap<K, V> createMultimapView() {
253     ImmutableMap<K, ImmutableSet<V>> map = viewValuesAsImmutableSet();
254     return new ImmutableSetMultimap<K, V>(map, map.size(), null);
255   }
256 
257   private ImmutableMap<K, ImmutableSet<V>> viewValuesAsImmutableSet() {
258     final Map<K, V> outer = this;
259     return new ImmutableMap<K, ImmutableSet<V>>() {
260       @Override
261       public int size() {
262         return outer.size();
263       }
264 
265       @Override
266       public ImmutableSet<V> get(@Nullable Object key) {
267         V outerValue = outer.get(key);
268         return outerValue == null ? null : ImmutableSet.of(outerValue);
269       }
270 
271       @Override
272       ImmutableSet<Entry<K, ImmutableSet<V>>> createEntrySet() {
273         return new ImmutableSet<Entry<K, ImmutableSet<V>>>() {
274           @Override
275           public UnmodifiableIterator<Entry<K, ImmutableSet<V>>> iterator() {
276             final Iterator<Entry<K,V>> outerEntryIterator = outer.entrySet().iterator();
277             return new UnmodifiableIterator<Entry<K, ImmutableSet<V>>>() {
278               @Override
279               public boolean hasNext() {
280                 return outerEntryIterator.hasNext();
281               }
282 
283               @Override
284               public Entry<K, ImmutableSet<V>> next() {
285                 final Entry<K, V> outerEntry = outerEntryIterator.next();
286                 return new AbstractMapEntry<K, ImmutableSet<V>>() {
287                   @Override
288                   public K getKey() {
289                     return outerEntry.getKey();
290                   }
291 
292                   @Override
293                   public ImmutableSet<V> getValue() {
294                     return ImmutableSet.of(outerEntry.getValue());
295                   }
296                 };
297               }
298             };
299           }
300 
301           @Override
302           boolean isPartialView() {
303             return false;
304           }
305 
306           @Override
307           public int size() {
308             return outer.size();
309           }
310         };
311       }
312 
313       @Override
314       boolean isPartialView() {
315         return false;
316       }
317     };
318   }
319 
320   ImmutableCollection<V> createValues() {
321     return new ImmutableMapValues<K, V>(this);
322   }
323 
324   @Override public boolean equals(@Nullable Object object) {
325     return Maps.equalsImpl(this, object);
326   }
327 
328   @Override public int hashCode() {
329     // not caching hash code since it could change if map values are mutable
330     // in a way that modifies their hash codes
331     return entrySet().hashCode();
332   }
333 
334   @Override public String toString() {
335     return Maps.toStringImpl(this);
336   }
337 }